package utils

import (
	"MoSkeleton/framework/core/moerrors"
	"fmt"
	"github.com/xuri/excelize/v2"
	"io"
	"reflect"
	"strconv"
)

const commonTitleKey = ":*"

type ImportExcelFile struct {
}

func NewImportExcelFile() *ImportExcelFile {
	return &ImportExcelFile{}
}

func (ef *ImportExcelFile) ImportFromReader(reader io.Reader, items any) error {
	excelFile, err := excelize.OpenReader(reader)
	if err != nil {
		return err
	}
	return ef.ImportFromExcelizeFile(excelFile, items)
}

func (ef *ImportExcelFile) ImportFromFile(filePath string, items any) error {
	excelFile, err := excelize.OpenFile(filePath)
	if err != nil {
		return err
	}
	return ef.ImportFromExcelizeFile(excelFile, items)
}
func (ef *ImportExcelFile) ImportFromExcelizeFile(file *excelize.File, items any) error {
	sheetName := file.GetSheetName(0)
	return ef.ImportFromExcelizeFileWithSheet(file, sheetName, items)
}

func (ef *ImportExcelFile) ImportFromExcelizeFileWithSheet(file *excelize.File, sheetName string, items any) error {
	rows, err := file.GetRows(sheetName)
	if err != nil {
		return err
	}
	itemsType := reflect.TypeOf(items)
	if itemsType.Kind() != reflect.Ptr && itemsType.Elem().Kind() != reflect.Slice {
		return moerrors.NewWithMsg("receive data type error")
	}

	elem := itemsType.Elem().Elem()
	headerFieldMap := ef.itemHeaderFieldMap(elem)
	firstRow := rows[0]
	itemsValue := reflect.ValueOf(items)
	//1: 跳过表头
	for _, row := range rows[1:] {
		itemObj := ef.setRowToItem(row, elem, headerFieldMap, firstRow)
		itemsValue.Elem().Set(reflect.Append(itemsValue.Elem(), itemObj.Elem()))
	}
	return nil
}
func (ef *ImportExcelFile) setRowToItem(row []string, rowType reflect.Type, headerFieldMap map[string]reflect.StructField, indexHeaders []string) reflect.Value {
	itemObj := reflect.New(rowType)
	for index, col := range row {
		header := indexHeaders[index]
		sf, ok := headerFieldMap[header]
		if !ok {
			if len(headerFieldMap) == 0 {
				sf, ok = headerFieldMap[commonTitleKey]
				if !ok {
					continue
				}
			}
		}
		ef.setItemFieldValue(itemObj.Elem(), sf, col)
	}
	return itemObj
}

func (ef *ImportExcelFile) setItemFieldValue(itemObj reflect.Value, sf reflect.StructField, col string) {
	if sf.Name == "" {
		ef.setFieldValue(itemObj, sf, col)
	} else {
		ef.setFieldValue(itemObj.FieldByName(sf.Name), sf, col)
	}
}

func (ef *ImportExcelFile) setFieldValue(fieldVal reflect.Value, sf reflect.StructField, col string) {
	if fieldVal.IsValid() && sf.Type != nil {
		switch sf.Type.Kind() {
		case reflect.String:
			fieldVal.SetString(col)
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			intVal, err := strconv.ParseInt(col, 10, 64)
			if err == nil {
				fieldVal.SetInt(intVal)
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			uintVal, err := strconv.ParseUint(col, 10, 64)
			if err == nil {
				fieldVal.SetUint(uintVal)
			}
		default:
			fmt.Printf("Unsupported field type: %v\n", sf.Type)
		}
	}
}

/*
 * 获取表头和字段的映射关系
 */
func (ef *ImportExcelFile) itemHeaderFieldMap(elem reflect.Type) map[string]reflect.StructField {
	ret := make(map[string]reflect.StructField)
	switch elem.Kind() {
	case reflect.Struct:
		for i := 0; i < elem.NumField(); i++ {
			title := elem.Field(i).Tag.Get("excel")
			if title == "" {
				title = elem.Field(i).Name
			}
			ret[title] = elem.Field(i)
		}
	default:
		sf := reflect.StructField{}
		sf.Type = elem
		ret[commonTitleKey] = sf
	}
	return ret
}
